﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using C4F.DevKit.Calendar.Components;

namespace C4F.DevKit.Calendar.Controls
{
    /// <summary>
    /// This control is used to configure an appointments with specific details to be stored.
    /// </summary>
    [ToolboxItem(true), ToolboxBitmap(typeof(AppointmentDetails))]
    public partial class AppointmentDetails : UserControl
    {
        #region Enums

        /// <summary>
        /// A type which specifies a time interval for the time combo boxes in this control
        /// </summary>
        public enum TimeIntervalType
        {
            /// <summary>
            /// Indicates that there is no specified time interval
            /// </summary>
            NotSet = 0,
            
            /// <summary>
            /// An interval of ten minutes
            /// </summary>
            Ten = 1,

            /// <summary>
            /// An interval of fifteen minutes
            /// </summary>
            Fifteen = 2,

            /// <summary>
            /// An interval of thirty minutes
            /// </summary>
            Thirty = 3,

            /// <summary>
            /// An interval of sixty minutes
            /// </summary>
            Sixty = 4
        }

        #endregion Enums

        #region Private Data

        private Appointment _appt;

        private const int DEFAULT_TIME_INTERVAL_IN_MINS = 30;
        private CheckBox[] _weekdayButtons;
        private CalendarService _calendarService;
        private TimeIntervalType _timeInterval;
        private bool _includeWindowsContactsAsAttendees = true;
        private List<Attendee> _attendees = new List<Attendee>();
        private const string DESIGNER_CATEGORY = "Calendar";

        #endregion Private Data

        #region Properties

        /// <summary>
        /// The appointment being displayed in the control.
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Appointment Appointment
        {
            get { PopulateAppointment(); return _appt; }

            set { _appt = value; PopulateControls(); }
        }

        /// <summary>
        /// The interval between times in the dropdown boxes for the start and end times.
        /// </summary>
        [Browsable(true), Category(DESIGNER_CATEGORY), Description("The interval populated in the start/end times dropdowns."), DefaultValue(TimeIntervalType.Fifteen)]
        public TimeIntervalType TimeInterval
        {
            get { return _timeInterval; }
            set { _timeInterval = value; }
        }

        /// <summary>
        /// The CalendarService to use with this control and its appointment.
        /// </summary>
        [Browsable(true), Category(DESIGNER_CATEGORY)]
        public CalendarService CalendarService
        {
            get { return _calendarService; }
            set
            {
                if (value != null)
                {
                    _calendarService = value;                   
                }
            }
        }

        /// <summary>
        /// Determines if the user's windows contacts should be included when choosing attendees.
        /// </summary>
        /// <remarks>The Contacts will only be shown if the CalendarService property is set.</remarks>
        [Browsable(true), DefaultValue(true), Description(@"Determines if the user's windows contacts should be included when choosing attendees. The Contacts will only be shown if the CalendarService property is set.")]
        public bool ShowWinContactsAsAttendees
        {
            get { return _includeWindowsContactsAsAttendees; }
            set { _includeWindowsContactsAsAttendees = value; }
        }

        /// <summary>
        /// The list that the user will pick from in order to add attendees to the appointment.
        /// </summary>
        /// <remarks>This is not the attendees that will be added to the actual appointment.</remarks>
        [Browsable(false), Category(DESIGNER_CATEGORY), Description("The list that the user will pick from in order to add attendees to the appointment.")]
        public List<Attendee> AttendeesList
        {
            get
            {
                if (_attendees == null)
                    _attendees = new List<Attendee>();

                return _attendees;
            }
        }

        #endregion Properties

        #region Constructor

        /// <summary>
        /// Creates a new AppointmentDetails control.
        /// </summary>
        public AppointmentDetails()
        {
            InitializeComponent();

            if (!DesignMode)
            {
                InitializeProperties();
                PopulateTimes();
                SetDateTimes();
                _appt = new Appointment();
            }

            _weekdayButtons = new CheckBox[] {chkSunday, chkMonday,
                chkTuesday, chkWednesday, chkThursday, chkFriday, chkSaturday};
            

        }

        #endregion Constructor

        #region Methods

        #region Event Handlers

        private void chkAllDay_CheckedChanged(object sender, EventArgs e)
        {
            OnCheckAllDay();
        }

        private void rdoWeekly_CheckedChanged(object sender, EventArgs e)
        {
            foreach (CheckBox weekdayButton in _weekdayButtons)
            {
                weekdayButton.Enabled = rdoWeekly.Checked;
                weekdayButton.Checked = false;
            }

            if (rdoWeekly.Checked)
            {
                int startWeekday = (int)dtStartDate.Value.DayOfWeek;
                _weekdayButtons[startWeekday].Checked = true;
            }
        }

        private void rdoNone_CheckedChanged(object sender, EventArgs e)
        {
            Control[] recurrenceControls = new Control[] {rdoNoEndDate,
                rdoRecurCount, rdoRecurEndDate, txtRecurCount, dtRecurEndDate};

            foreach (Control recControl in recurrenceControls)
            {
                recControl.Enabled = !rdoNone.Checked;
            }
        }

        private void btnAttendees_Click(object sender, EventArgs e)
        {
            PopulateAttendeesWithWinContacts();

            using (SelectAttendees form = new SelectAttendees(AttendeesList, txtAttendees.Text))
            {
                if (form.ShowDialog() == DialogResult.OK)
                {
                    this.txtAttendees.Text = form.SelectedAttendees;
                }
            }
        }

        #endregion Event Handlers

        #region Private

        private void OnCheckAllDay()
        {
            cboStartTime.Enabled = cboEndTime.Enabled = !chkAllDay.Checked;
        }

        private void InitializeProperties()
        {
            TimeInterval = TimeIntervalType.Thirty;
        }

        private bool ValidateTimeFormat()
        {
            Regex timeRegex = new Regex("\\d?\\d:\\d\\d (am|pm)", RegexOptions.IgnoreCase);
            return timeRegex.Match(cboStartTime.Text).Success &&
                timeRegex.Match(cboEndTime.Text).Success;
        }

        private bool ValidateCountFormat()
        {
            return Regex.Match(txtRecurCount.Text, "\\d+").Success;
        }

        private void PopulateAppointment()
        {
            _appt.Attendees = ParseAttendees();

            TimeZone localZone = TimeZone.CurrentTimeZone;
            _appt.StartDateTime = localZone.ToUniversalTime(DateTime.Parse(string.Format("{0} {1}", dtStartDate.Value.ToShortDateString(), cboStartTime.Text)));
            _appt.EndDateTime = localZone.ToUniversalTime(DateTime.Parse(string.Format("{0} {1}", dtEndDate.Value.ToShortDateString(), cboEndTime.Text)));
            _appt.IsAllDayEvent = chkAllDay.Checked;
            _appt.Location = txtLocation.Text;
            _appt.Notes = txtNotes.Text.Replace("\r", "");
            Console.WriteLine(txtNotes.Text);
            _appt.Summary = txtSubject.Text;

            if (!rdoNone.Checked)
            {
                if (rdoDaily.Checked)
                {
                    _appt.SetDailyRecurrence();
                }
                else if (rdoWeekly.Checked)
                {
                    List<Weekday> weekdays = new List<Weekday>();

                    for (int i = 0; i < _weekdayButtons.Length; i++)
                    {
                        if (_weekdayButtons[i].Checked)
                        {
                            weekdays.Add((Weekday)i);
                        }
                    }

                    _appt.SetWeeklyRecurrence(weekdays);
                }
                else if (rdoMonthly.Checked)
                {
                    _appt.SetMonthlyRecurrence();
                }
                else if (rdoYearly.Checked)
                {
                    _appt.SetYearlyRecurrence();
                }

                if (rdoRecurCount.Checked)
                {
                    _appt.RecurrenceRule.Count = Int32.Parse(txtRecurCount.Text);
                }
                else if (rdoRecurEndDate.Checked)
                {
                    _appt.RecurrenceRule.EndDateTime = localZone.ToUniversalTime(dtRecurEndDate.Value);
                }
            }
        }

        private void PopulateTimes()
        {
            DateTime date = DateTime.Today;
            int interval = TimeIntervalInMinutes();            
            DateTime currentDateTime = date;

            while (currentDateTime.Date == date)
            {
                cboStartTime.Items.Add(currentDateTime.ToShortTimeString());
                cboEndTime.Items.Add(currentDateTime.ToShortTimeString());

                currentDateTime = currentDateTime.AddMinutes(interval);
            }
        }

        private int TimeIntervalInMinutes()
        {
            switch (TimeInterval)
            {
                case TimeIntervalType.Ten:
                    return 10;
                case TimeIntervalType.Fifteen:
                    return 15;
                case TimeIntervalType.Thirty:
                    return 30;
                case TimeIntervalType.Sixty:
                    return 60;
                default:
                    return DEFAULT_TIME_INTERVAL_IN_MINS;
            }
        }

        private void PopulateControls()
        {
            //Populate Summary section
            List<string> attendeeStrings = new List<string>();

            foreach (Attendee attendee in _appt.Attendees)
            {
                string attendeeString = attendee.Name;

                if (!string.IsNullOrEmpty(attendee.EmailAddress))
                {
                    attendeeString += " <" + attendee.EmailAddress + ">";
                }

                attendeeStrings.Add(attendeeString);
            }

            txtAttendees.Text = string.Join("; ", attendeeStrings.ToArray());
            txtSubject.Text = _appt.Summary;
            txtLocation.Text = _appt.Location;

            if (_appt.Notes != null)
            {
                txtNotes.Text = _appt.Notes.Replace("\n", "\r\n");
            }

            // populate Date/Time section
            chkAllDay.Checked = _appt.IsAllDayEvent;
            TimeZone localZone = TimeZone.CurrentTimeZone;
            DateTime localStart = localZone.ToLocalTime(_appt.StartDateTime);
            DateTime localEnd = localZone.ToLocalTime(_appt.EndDateTime);
            dtStartDate.Value = localStart.Date;
            dtEndDate.Value = localEnd.Date;
            SetComboBoxText(cboStartTime, localStart.ToShortTimeString());
            SetComboBoxText(cboEndTime, localEnd.ToShortTimeString());

            //Populate Recurrence section
            RecurrenceRule recRule = _appt.RecurrenceRule;

            if (recRule == null)
            {
                rdoNone.Checked = true;
                return;
            }


            if (recRule.EndDateTime == new DateTime())
            {
                if (recRule.Count == 0)
                {
                    rdoNoEndDate.Checked = true;
                }
                else
                {
                    rdoRecurCount.Checked = true;
                    txtRecurCount.Text = recRule.Count.ToString();
                }
            }
            else
            {
                rdoRecurEndDate.Checked = true;
                dtRecurEndDate.Value = localZone.ToLocalTime(recRule.EndDateTime);
            }

            switch (recRule.Frequency)
            {
                case Frequency.Daily:
                    rdoDaily.Checked = true;
                    break;
                case Frequency.Weekly:
                    rdoWeekly.Checked = true;
                    PopulateWeekdays();
                    break;
                case Frequency.Monthly:
                    rdoMonthly.Checked = true;
                    break;
                case Frequency.Yearly:
                    rdoYearly.Checked = true;
                    break;
                default:
                    rdoOther.Checked = true;
                    break;
            }
        }
        
        private void PopulateWeekdays()
        {
            if (_appt.RecurOnSunday)
            {
                chkSunday.Checked = true;
            }

            if (_appt.RecurOnMonday)
            {
                chkMonday.Checked = true;
            }

            if (_appt.RecurOnTuesday)
            {
                chkTuesday.Checked = true;
            }

            if (_appt.RecurOnWednesday)
            {
                chkWednesday.Checked = true;
            }

            if (_appt.RecurOnThursday)
            {
                chkThursday.Checked = true;
            }

            if (_appt.RecurOnFriday)
            {
                chkFriday.Checked = true;
            }

            if (_appt.RecurOnSaturday)
            {
                chkSaturday.Checked = true;
            }
        }

        private void SetComboBoxText(ComboBox comboBox, string text)
        {
            if (!comboBox.Items.Contains(text))
            {
                comboBox.Items.Add(text);
            }

            comboBox.Text = text;
        }

        private void SetDateTimes()
        {
            DateTime startDateTime = DateTime.Now;
            TimeSpan newTime = RoundUpTime(startDateTime.TimeOfDay);
            Console.WriteLine("timespan: " + newTime.ToString());
            Console.WriteLine("old start time: " + startDateTime.ToString());
            startDateTime = startDateTime.Subtract(startDateTime.TimeOfDay);
            startDateTime = startDateTime.Add(newTime);                
            DateTime endDateTime = startDateTime.AddHours(1);

            dtStartDate.Value = startDateTime.Date;
            dtEndDate.Value = endDateTime.Date;
            SetComboBoxText(cboStartTime, startDateTime.ToShortTimeString());
            SetComboBoxText(cboEndTime, endDateTime.ToShortTimeString());
        }

        private TimeSpan RoundUpTime(TimeSpan time)
        {
            int interval = TimeIntervalInMinutes();
            int minuteRemainder = time.Minutes % interval;
            //Console.WriteLine("minute rem: " + minuteRemainder

            if (minuteRemainder == 0)
            {
                return time;
            }

            return new TimeSpan(time.Hours, time.Minutes + interval - minuteRemainder, 0);
        }

        private List<Attendee> ParseAttendees()
        {
            string[] attendeeStrings = txtAttendees.Text.Split(';');

            // one non-whitespace character, optionally followed by any sequence of characters
            // ending with non-whitespace, excluding < for the e-mail address
            string namePattern = "[^\\s<](?:[^<]*[^\\s<])?";
            string emailPattern = "<([^>]+)>";
            Regex attendeeRegex = new Regex(String.Format("^\\s*({0})\\s*(?:{1})?", namePattern, emailPattern));

            List<Attendee> attendees = new List<Attendee>();

            foreach (string attendeeString in attendeeStrings)
            {
                if (string.IsNullOrEmpty(attendeeString))
                {
                    continue;
                }

                Match match = attendeeRegex.Match(attendeeString);
                string name = match.Groups[1].Value.Trim();
                string emailAddress = match.Groups[2].Value.Trim();

                if (string.IsNullOrEmpty(name))
                {
                    continue;
                }

                if (string.IsNullOrEmpty(emailAddress))
                {
                    emailAddress = name;
                }

                attendees.Add(new Attendee(name, emailAddress));
            }

            return attendees;
        }

        

        private void PopulateAttendeesWithWinContacts()
        {
            if (ShowWinContactsAsAttendees && _calendarService != null)
            {
                AttendeesList.Clear();
                AttendeesList.AddRange(_calendarService.GetAllWindowsContactsAsAttendees());
            }
        }

        private void ClearAttendees()
        {
            AttendeesList.Clear();
        }

        #endregion Private

        #endregion Methods
    }
}
